package com.example.firealarm.server;

import com.example.firealarm.model.FireAlarmEvent;
import com.example.firealarm.model.FireMessage;
import com.example.firealarm.protocol.FireProtocolParser;
import com.example.firealarm.service.FireAlarmEventHandler;
import com.example.firealarm.service.FireAlarmNotificationService;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

/**
 * 消防报警服务器
 * 基于Netty实现，用于接收消防设备的TCP连接和消息
 */
@Slf4j
@Component
public class FireAlarmServer {

    @Value("${fire-alarm.server.port:9000}")
    private int port;

    @Value("${fire-alarm.server.read-timeout:60000}")
    private long readTimeout;

    @Value("${fire-alarm.server.idle-timeout:180000}")
    private long idleTimeout;

    @Autowired
    private FireProtocolParser protocolParser;

    @Autowired
    private FireAlarmEventHandler eventHandler;

    @Autowired
    private FireAlarmNotificationService notificationService;

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private Channel serverChannel;

    // 存储设备连接信息
    private final ConcurrentMap<String, Channel> deviceChannels = new ConcurrentHashMap<>();

    /**
     * 启动服务器
     */
    @PostConstruct
    public void start() {
        try {
            bossGroup = new NioEventLoopGroup(1);
            workerGroup = new NioEventLoopGroup();

            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            // 空闲检测处理器
                            pipeline.addLast(new IdleStateHandler(readTimeout / 1000, 0, idleTimeout / 1000, TimeUnit.SECONDS));
                            // 消防协议解码器
                            pipeline.addLast(new FireProtocolDecoder());
                            // 消防消息处理器
                            pipeline.addLast(new FireMessageHandler());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            // 绑定端口并启动服务器
            ChannelFuture future = bootstrap.bind(port).sync();
            serverChannel = future.channel();
            log.info("消防报警服务器已启动，监听端口：{}", port);
        } catch (Exception e) {
            log.error("启动消防报警服务器失败", e);
            shutdown();
        }
    }

    /**
     * 关闭服务器
     */
    @PreDestroy
    public void shutdown() {
        try {
            if (serverChannel != null) {
                serverChannel.close().sync();
            }
            if (bossGroup != null) {
                bossGroup.shutdownGracefully();
            }
            if (workerGroup != null) {
                workerGroup.shutdownGracefully();
            }
            log.info("消防报警服务器已关闭");
        } catch (Exception e) {
            log.error("关闭消防报警服务器异常", e);
        }
    }

    /**
     * 消防协议解码器
     * 负责从字节流中解析出完整的消防协议消息
     */
    private class FireProtocolDecoder extends ByteToMessageDecoder {

        @Override
        protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
            // 标记当前读取位置
            in.markReaderIndex();

            // 检查是否有足够的字节可读
            if (in.readableBytes() < 5) { // 至少需要起始符(1) + 长度(2) + 校验和(1) + 结束符(1)
                return;
            }

            // 检查起始符
            byte startMark = in.getByte(in.readerIndex());
            if (startMark != 0x40) { // '@'
                // 不是有效的起始符，丢弃一个字节并继续
                in.skipBytes(1);
                return;
            }

            // 读取消息长度（不包括起始符、长度字段和结束符）
            int messageLength = ((in.getByte(in.readerIndex() + 1) & 0xFF) << 8) | (in.getByte(in.readerIndex() + 2) & 0xFF);

            // 检查消息长度是否合理
            if (messageLength <= 0 || messageLength > 1024) {
                // 长度不合理，丢弃一个字节并继续
                in.skipBytes(1);
                return;
            }

            // 检查是否有完整的消息
            int totalLength = messageLength + 5; // 起始符(1) + 长度(2) + 内容 + 校验和(1) + 结束符(1)
            if (in.readableBytes() < totalLength) {
                // 消息不完整，重置读取位置并等待更多数据
                in.resetReaderIndex();
                return;
            }

            // 检查结束符
            byte endMark = in.getByte(in.readerIndex() + totalLength - 1);
            if (endMark != 0x23) { // '#'
                // 不是有效的结束符，丢弃一个字节并继续
                in.skipBytes(1);
                return;
            }

            // 读取完整的消息
            byte[] messageData = new byte[totalLength];
            in.readBytes(messageData);

            // 将字节数组传递给下一个处理器
            out.add(messageData);
        }
    }

    /**
     * 消防消息处理器
     * 负责处理解码后的消防协议消息
     */
    private class FireMessageHandler extends ChannelInboundHandlerAdapter {

        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            String remoteAddress = ctx.channel().remoteAddress().toString();
            log.info("设备连接：{}", remoteAddress);
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) {
            String remoteAddress = ctx.channel().remoteAddress().toString();
            log.info("设备断开连接：{}", remoteAddress);

            // 从设备列表中移除
            deviceChannels.values().removeIf(channel -> channel.equals(ctx.channel()));
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            if (msg instanceof byte[]) {
                byte[] data = (byte[]) msg;
                processMessage(ctx, data);
            }
        }

        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
            if (evt instanceof IdleStateEvent) {
                // 处理空闲超时事件
                IdleStateEvent event = (IdleStateEvent) evt;
                String remoteAddress = ctx.channel().remoteAddress().toString();

                switch (event.state()) {
                    case READER_IDLE:
                        log.warn("设备读取超时：{}", remoteAddress);
                        break;
                    case ALL_IDLE:
                        log.warn("设备空闲超时，关闭连接：{}", remoteAddress);
                        ctx.close();
                        break;
                }
            }
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            log.error("消防消息处理异常：{}", cause.getMessage());
            ctx.close();
        }

        /**
         * 处理消防协议消息
         */
        private void processMessage(ChannelHandlerContext ctx, byte[] data) {
            try {
                // 解析消息
                FireMessage message = protocolParser.parseMessage(data);
                if (message == null) {
                    log.error("解析消息失败");
                    return;
                }

                // 记录设备连接信息
                String deviceAddress = message.getSourceAddress();
                deviceChannels.put(deviceAddress, ctx.channel());

                // 处理消息并转换为报警事件
                FireAlarmEvent event = eventHandler.handleMessage(message);
                if (event != null) {
                    // 通知报警事件
                    notificationService.notifyAlarmEvent(event);
                }

                // 发送响应消息
                sendResponse(ctx, message);
            } catch (Exception e) {
                log.error("处理消防消息异常", e);
            }
        }

        /**
         * 发送响应消息
         */
        private void sendResponse(ChannelHandlerContext ctx, FireMessage request) {
            try {
                // 构建响应消息（简单的确认消息）
                byte[] response = protocolParser.buildMessage(
                        request.getTargetType(),  // 源地址类型（接收方变为发送方）
                        request.getTargetAddress(), // 源地址
                        request.getSourceType(),    // 目标地址类型（发送方变为接收方）
                        request.getSourceAddress(), // 目标地址
                        request.getSerialNumber(),  // 保持相同的流水号
                        (byte) (request.getCommand() + 0x80), // 命令字+0x80表示响应
                        new byte[]{0x00}            // 简单的确认数据
                );

                if (response != null) {
                    // 发送响应
                    ctx.writeAndFlush(Unpooled.wrappedBuffer(response));
                }
            } catch (Exception e) {
                log.error("发送响应消息异常", e);
            }
        }
    }

    /**
     * 向指定设备发送消息
     *
     * @param deviceAddress 设备地址
     * @param data 消息数据
     * @return 是否发送成功
     */
    public boolean sendMessage(String deviceAddress, byte[] data) {
        try {
            Channel channel = deviceChannels.get(deviceAddress);
            if (channel != null && channel.isActive()) {
                channel.writeAndFlush(Unpooled.wrappedBuffer(data));
                return true;
            }
            return false;
        } catch (Exception e) {
            log.error("发送消息异常", e);
            return false;
        }
    }
}